﻿namespace IBTrader.Modules.Market
{
    using AxTWSLib;
    using IBTrader.Model;
    using IBTrader.Modules.Tws;
    using IBTrader.Properties;
    using NLog;
    using System;
    using System.Linq;

    class Contract
    {
        private readonly IIB ib;
        private readonly Store store;
        private readonly static Logger logger = LogManager.GetCurrentClassLogger();
        public event EventHandler<ContractArgs> Hit;
        public Contract(IIB ib, Store store)
        {
            this.ib = ib;
            this.store = store;
        }
        public void LoadFromSettings(object _, EventArgs __)
        {
            Settings.Default.Futures.ForEach(contract => {
                var row =store.Contracts.SingleOrDefault(c => c.Symbol.Equals(contract.Symbol));
                if (row == null)
                {
                    row = new Model.Contract(store) { Symbol = contract.Symbol };
                    store.Contracts.Add(row);                
                }
                Request(row, ib.CreateContract(contract.Symbol, "FUT", contract.Exchange, "USD", contract.Multiplier) as IBApi.Contract);
            });
        }
        public void Load(object _, EventArgs __)
        {
            store.Contracts.ToList().ForEach(f => Request(f, ib.CreateContract(f.Symbol, f.SecurityType, f.Exchange, f.Currency, f.Multiplier) as IBApi.Contract));
        }
        private void Request(Model.Contract row, IBApi.Contract contract)
        {
            row.ReqEnd = false;
            logger.Debug("reqContractDetails {0} {1} {2} {3}", row.Id, contract.Symbol, contract.Exchange, contract.Expiry);
            ib.reqContractDetails(row.Id, contract);
        }
        public void Response(object sender, ContractDetailsEventArgs e)
        {
            var contractDetails = e.ContractDetails as IBApi.ContractDetails;
            var contract = contractDetails.Summary as IBApi.Contract;
            var row = store.Contracts.Single(r => r.Id == e.RequestId);
            var exchange = string.IsNullOrWhiteSpace(contract.PrimaryExch) ? contract.Exchange : contract.PrimaryExch;

            Func<string, string, string> merge = (x, y) => (x == null || x == y) ? y : x + "," + y;
            if (!row.ReqEnd)
            {
                row.Exchange = merge(row.Exchange, exchange);
                row.Currency = merge(row.Currency, contract.Currency);
                row.Expiry = merge(row.Expiry, contractDetails.ContractMonth);
                row.SecurityType = merge(row.SecurityType, contract.SecType);
                row.Multiplier = merge(row.Multiplier, contract.Multiplier);
//                row.PriceMagnifier = (row.IsPriceMagnifierNull() ? "" : row.PriceMagnifier + ",") + e.contractDetails.priceMagnifier;
//                row.LiquidHours = (row.IsLiquidHoursNull() ? "" : row.LiquidHours + ",") + e.contractDetails.liquidHours;
//                row.TradingHours = (row.IsTradingHoursNull() ? "" : row.TradingHours + ",") + e.contractDetails.tradingHours;
                row.MinTick = (decimal)contractDetails.MinTick;
            }
            logger.Debug("contractDetailsEx reqId {0} {1} {2} symbol {3} localSymbol {4} marketName {5}", e.RequestId, contractDetails.LongName,
                contractDetails.ContractMonth, contract.Symbol, contract.LocalSymbol, contractDetails.MarketName);
        }
        public void End(object sender, _DTwsEvents_contractDetailsEndEvent e)
        {
            var row = store.Contracts.Single(r => r.Id == e.reqId);
            row.ReqEnd = true;
            logger.Info("Contract loaded {0}", row);
            if (Hit != null) Hit(this, new ContractArgs { Contract = row });
        }
        public void RequestMarketData(object _, ContractArgs e)
        {
            logger.Debug("RequestMarketData {0} {1}", e.Contract.Id, e.Contract.Symbol);
            var contract = ib.CreateContract(e.Contract.Symbol, e.Contract.SecurityType, e.Contract.Exchange, e.Contract.Currency) as IBApi.Contract;
            contract.Expiry = e.Contract.Expiry;
            contract.Multiplier = e.Contract.Multiplier;
            ib.reqMktData(e.Contract.Id, contract, "", false);
            //ib.reqRealTimeBarsEx(e.Contract.Id, contract, 5, BarType.TRADES.ToString(), 0);
        }
        public void ReRequestMarketData(object _, EventArgs __) // unused
        {
            foreach (var contract in store.Contracts.Where(c => c.ReqEnd))
            {
                logger.Info("ReRequestMarketData {0} {1}", contract.Id, contract.Symbol);
                ib.cancelRealTimeBars(contract.Id);
                ib.cancelMktData(contract.Id);
                RequestMarketData(this, new ContractArgs { Contract = contract });
            }
        }
        public void UpdateFutures(object _, EventArgs __) // unused
        {
            foreach (var contract in store.Contracts)
            {
                var expiration = Expirations.Expiration(contract.Symbol, DateTime.UtcNow);
                if (contract.Expiry != expiration)
                {
                    logger.Info("FutureExpiry changed {0} => {1}", contract.Expiry, expiration);
                    store.Contracts.Remove(contract);
                }
            }
        }
        public class ContractArgs : EventArgs
        {
            public Model.Contract Contract { get; set; }
        }
    }
}